<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" >

<title>Vue笔记（2） | 没想好的个人博客</title>

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

<!-- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous"> -->
   <!-- Font Awesome CSS-->
   <link href="https://cdn.bootcss.com/font-awesome/5.13.0/css/all.css" rel="stylesheet">

   <link rel="stylesheet" href="https://cdn.bootcss.com/font-awesome/5.7.2/css/all.min.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">

<link rel="shortcut icon" href="https://shixixiyue.gitee.io//favicon.ico?v=1709791195950">
<link rel="stylesheet" href="https://shixixiyue.gitee.io//styles/main.css">



<link rel="stylesheet" href="https://unpkg.com/aos@next/dist/aos.css" />
<script src="https://jsd.onmicrosoft.cn/npm/vue/dist/vue.js"></script>


<style>
  .hljs-ln{
    padding-bottom: 1.2rem;
    margin: 0rem !important;
  }
  .hljs-ln td {
    border: none !important;
    padding: 2px 6px !important;
    line-height: 1.2rem;
    font-size: 1rem;
  }
  .hljs-ln tr td:first-child  {
    color: cadetblue;
    border-right: 1px solid !important;
  }
  .hljs-ln tr {
    border: none !important;
  }
</style>

    <meta name="description" content="
开源了，有代码管理的地方，服务端搬到了阿里云，数据库在腾讯云...。启用了GZIP压缩，打包也坑，一个Echarts 2.8M（CDN了）









1.下拉树
2.dialog









开源地址

1.下拉树
elem..." />
    <meta name="keywords" content="Vue笔记" />
  </head>
  <body>
    <div id="app" class="main">

      <div class="sidebar" :class="{ 'full-height': menuVisible }">
  <div class="top-container" data-aos="fade-right">
    <div class="top-header-container">
      <a class="site-title-container" href="https://shixixiyue.gitee.io/">
        <img src="https://shixixiyue.gitee.io//images/avatar.png?v=1709791195950" class="site-logo">
        <h1 class="site-title">没想好的个人博客</h1>
      </a>
      <div class="menu-btn" @click="menuVisible = !menuVisible">
        <div class="line"></div>
      </div>
    </div>
    <div>
      
        
          <a href="https://shixixiyue.gitee.io/" class="site-nav">
            首页
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tag/4gOKry8Qv/" class="site-nav">
            .Net Core
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tag/njETBpqXu/" class="site-nav">
            FineUICore
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tag/XyXyeLOBB" class="site-nav">
            读书笔记
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tag/TyVX6hfCJ" class="site-nav">
            Vue笔记
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tag/3Hz2sPtzO/" class="site-nav">
            产品
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tag/Db1wShB_E" class="site-nav">
            其他
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io/" class="site-nav">
            一一一一一一一一一
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//archives" class="site-nav">
            文章归档
          </a>
        
      
        
          <a href="https://shixixiyue.gitee.io//tags" class="site-nav">
            全部标签
          </a>
        
      
    </div>
  </div>
  <div class="bottom-container" data-aos="flip-up" data-aos-offset="0">
    <div class="social-container">
      
        
      
        
      
        
      
        
      
        
      
    </div>
    <div class="site-description">
      没事啥都说
    </div>
    <div class="site-footer">
      QQ：<a href="tencent://message/?uin=935732994&Site=''&Menu=yes">935732994<a/> <a href="http://beian.miit.gov.cn">京ICP备2024045283号<a/> | <a class="rss" href="https://shixixiyue.gitee.io//atom.xml" target="_blank">RSS</a>
    </div>
  </div>
</div>


      <div class="main-container">
        <div class="content-container" data-aos="fade-up">
          <div class="post-detail">
            <h2 class="post-title">Vue笔记（2）</h2>
            <div class="post-date">2020-04-28
            
                
                  <a href="https://shixixiyue.gitee.io/tag/TyVX6hfCJ/" class="tag">
                    Vue笔记
                  </a>
                
            </div>
            
            <div class="post-content" v-pre>
              <blockquote>
<p>开源了，有代码管理的地方，服务端搬到了阿里云，数据库在腾讯云...。启用了GZIP压缩，打包也坑，一个Echarts 2.8M（CDN了）</p>
</blockquote>
<!-- more -->
<p><ul class="markdownIt-TOC">
<li>
<ul>
<li>
<ul>
<li>
<ul>
<li><a href="#1%E4%B8%8B%E6%8B%89%E6%A0%91">1.下拉树</a></li>
<li><a href="#2dialog">2.dialog</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</p>
<blockquote>
<p><a href="https://gitee.com/shixixiyue/VueAppBox">开源地址</a></p>
</blockquote>
<h4 id="1下拉树">1.下拉树</h4>
<p>elementui的下拉树很常用，一搜一大把，随便<a href="https://blog.csdn.net/sleepwalker_1992/article/details/87894588">找了一个抄的</a>，学习了一下改了改，多选还没试过</p>
<ul>
<li>加入v-model</li>
<li>加入异步延迟加载后重新选择赋值</li>
<li>加入树的自定义属性</li>
<li>加入禁用属性的优化</li>
<li>修改width算法</li>
</ul>
<pre><code>&lt;template&gt;
  &lt;div&gt;
    &lt;div
      class=&quot;mask&quot;
      v-show=&quot;isShowSelect&quot;
      @click=&quot;isShowSelect = !isShowSelect&quot;
    &gt;&lt;/div&gt;
    &lt;el-popover
      placement=&quot;bottom-start&quot;
      :width=&quot;width&quot;
      trigger=&quot;manual&quot;
      v-model=&quot;isShowSelect&quot;
      @hide=&quot;popoverHide&quot;
    &gt;
      &lt;el-tree
        class=&quot;common-tree&quot;
        :style=&quot;style&quot;
        ref=&quot;tree&quot;
        :data=&quot;TreeData&quot;
        :props=&quot;defaultProps&quot;
        :show-checkbox=&quot;multiple&quot;
        :node-key=&quot;idKey&quot;
        :check-strictly=&quot;checkStrictly&quot;
        default-expand-all
        :expand-on-click-node=&quot;false&quot;
        :check-on-click-node=&quot;multiple&quot;
        :highlight-current=&quot;true&quot;
        @node-click=&quot;handleNodeClick&quot;
        @check-change=&quot;handleCheckChange&quot;
      &gt;
      &lt;/el-tree&gt;
      &lt;el-select
        :style=&quot;selectStyle&quot;
        slot=&quot;reference&quot;
        ref=&quot;select&quot;
        :size=&quot;size&quot;
        :value=&quot;selectedData&quot;
        :multiple=&quot;multiple&quot;
        :clearable=&quot;clearable&quot;
        :collapse-tags=&quot;collapseTags&quot;
        @click.native=&quot;isShowSelect = !isShowSelect&quot;
        @remove-tag=&quot;removeSelectedNodes&quot;
        @clear=&quot;removeSelectedNode&quot;
        @change=&quot;changeSelectedNodes&quot;
        class=&quot;tree-select&quot;
        v-bind=&quot;$attrs&quot;
      &gt;
        &lt;el-option
          v-for=&quot;item in options&quot;
          :key=&quot;item.value&quot;
          :label=&quot;item.label&quot;
          :value=&quot;item.value&quot;
        &gt;
        &lt;/el-option&gt;
      &lt;/el-select&gt;
    &lt;/el-popover&gt;
  &lt;/div&gt;
&lt;/template&gt;
&lt;script&gt;
import { isArray, isArrayEmpty } from &quot;@/utils/validate&quot;;
export default {
  name: &quot;select-tree&quot;,
  props: {
    // 树结构数据
    data: {
      type: Array,
      default() {
        return [];
      }
    },
    defaultProps: {
      type: Object,
      default() {
        return { children: &quot;children&quot;, label: &quot;text&quot; };
      }
    },
    // 配置是否可多选
    multiple: {
      type: Boolean,
      default() {
        return false;
      }
    },
    // 配置是否可清空选择
    clearable: {
      type: Boolean,
      default() {
        return false;
      }
    },
    // 配置多选时是否将选中值按文字的形式展示
    collapseTags: {
      type: Boolean,
      default() {
        return false;
      }
    },
    idKey: {
      type: String,
      default() {
        return &quot;id&quot;;
      }
    },
    nameKey: String,
    pIdKey: String,
    // 显示复选框情况下，是否严格遵循父子不互相关联
    checkStrictly: {
      type: Boolean,
      default() {
        return false;
      }
    },
    size: {
      type: String,
      default() {
        return &quot;small&quot;;
      }
    },
    width: {
      type: String,
      default() {
        return &quot;250px&quot;;
      }
    },
    height: {
      type: Number,
      default() {
        return 300;
      }
    },
    selectedVal: {
      type: [String, Number, Array],
      default() {
        return &quot;&quot;;
      }
    }
  },
  data() {
    return {
      isShowSelect: false, // 是否显示树状选择器
      options: [],
      selectedData: &quot;&quot;, // 选中的节点
      style: &quot;height:&quot; + this.height + &quot;px;&quot;,
      selectStyle: `width:calc(${this.width});`,
      checkedIds: [],
      checkedData: []
    };
  },
  mounted() {
    this.initCheckedData();
  },
  methods: {
    // 单选时点击tree节点，设置select选项
    setSelectOption(node) {
      let tmpMap = {};
      tmpMap.value = node.key;
      tmpMap.label = node.label;
      this.options = [];
      this.options.push(tmpMap);
      this.$nextTick(() =&gt; {
        this.selectedData = node.key;
      });
    },
    // 单选，选中传进来的节点
    checkSelectedNode(checkedKeys) {
      var item = checkedKeys[0];
      this.$refs.tree.setCurrentKey(item);
      var node = this.$refs.tree.getNode(item.toString());
      if (node) {
        this.setSelectOption(node);
      }
    },
    // 多选，勾选上传进来的节点
    checkSelectedNodes(checkedKeys) {
      this.$refs.tree.setCheckedKeys(checkedKeys);
    },
    // 单选，清空选中
    clearSelectedNode() {
      this.selectedData = &quot;&quot;;
      this.$refs.tree.setCurrentKey(null);
    },
    // 多选，清空所有勾选
    clearSelectedNodes() {
      var checkedKeys = this.$refs.tree.getCheckedKeys(); // 所有被选中的节点的 key 所组成的数组数据
      for (let i = 0; i &lt; checkedKeys.length; i++) {
        this.$refs.tree.setChecked(checkedKeys[i], false);
      }
    },
    initCheckedData() {
      if (this.multiple) {
        // 多选
        if (!isArrayEmpty(this.checkedKeys)) {
          this.checkSelectedNodes(this.checkedKeys);
        } else {
          this.clearSelectedNodes();
        }
      } else {
        // 单选
        if (!isArrayEmpty(this.checkedKeys)) {
          this.checkSelectedNode(this.checkedKeys);
        } else {
          this.clearSelectedNode();
        }
      }
    },
    popoverHide() {
      if (this.multiple) {
        this.checkedIds = this.$refs.tree.getCheckedKeys(); // 所有被选中的节点的 key 所组成的数组数据
        this.checkedData = this.$refs.tree.getCheckedNodes(); // 所有被选中的节点所组成的数组数据
      } else {
        this.checkedIds = this.$refs.tree.getCurrentKey();
        this.checkedData = this.$refs.tree.getCurrentNode();
      }
      this.$emit(&quot;popoverHide&quot;, this.checkedIds, this.checkedData);
    },
    // 单选，节点被点击时的回调,返回被点击的节点数据
    handleNodeClick(data, node) {
      if (!this.multiple) {
        this.setSelectOption(node);
        this.isShowSelect = !this.isShowSelect;
        // this.$nextTick(()=&gt;{})
        this.$emit(&quot;change&quot;, node.key);
      }
    },
    // 多选，节点勾选状态发生变化时的回调
    handleCheckChange() {
      var checkedKeys = this.$refs.tree.getCheckedKeys(); // 所有被选中的节点的 key 所组成的数组数据
      this.options = checkedKeys.map(item =&gt; {
        var node = this.$refs.tree.getNode(item); // 所有被选中的节点对应的node
        let tmpMap = {};
        tmpMap.value = node.key;
        tmpMap.label = node.label;
        return tmpMap;
      });
      this.selectedData = this.options.map(item =&gt; {
        return item.value;
      });
      this.$emit(&quot;change&quot;, this.selectedData);
    },
    // 多选,删除任一select选项的回调
    removeSelectedNodes(val) {
      this.$refs.tree.setChecked(val, false);
      var node = this.$refs.tree.getNode(val);
      if (!this.checkStrictly &amp;&amp; node.childNodes.length &gt; 0) {
        this.treeToList(node).map(item =&gt; {
          if (item.childNodes.length &lt;= 0) {
            this.$refs.tree.setChecked(item, false);
          }
        });
        this.handleCheckChange();
      }
      this.$emit(&quot;change&quot;, this.selectedData);
    },
    treeToList(tree) {
      var queen = [];
      var out = [];
      queen = queen.concat(tree);
      while (queen.length) {
        var first = queen.shift();
        if (first.childNodes) {
          queen = queen.concat(first.childNodes);
        }
        out.push(first);
      }
      return out;
    },
    // 单选,清空select输入框的回调
    removeSelectedNode() {
      this.clearSelectedNode();
      this.$emit(&quot;change&quot;, this.selectedData);
    },
    // 选中的select选项改变的回调
    changeSelectedNodes(selectedData) {
      // 多选,清空select输入框时，清除树勾选
      if (this.multiple &amp;&amp; selectedData.length &lt;= 0) {
        this.clearSelectedNodes();
      }
      this.$emit(&quot;change&quot;, this.selectedData);
    }
  },
  model: {
    prop: &quot;selectedVal&quot;,
    event: &quot;change&quot;
  },
  computed: {
    //组装Treedata
    TreeData() {
      if (this.data.length == 0) {
        return [];
      }
      let { data, idKey, pIdKey, nameKey } = this;
      if (!pIdKey) {
        return this.data;
      }
      let _treedata = [];
      // id: 1,label: '一级 1', children: [{
      let getChildren = item =&gt; {
        let thisc = data.search({ [pIdKey]: item[idKey] });
        thisc.forEach(thisc_item =&gt; {
          thisc_item.children = getChildren(thisc_item);
        });
        return thisc;
      };
      //得到子菜单
      data.forEach(item =&gt; {
        //是否有子
        let c = data.search({ [pIdKey]: item[idKey] });
        if (c.length != 0) {
          item.children = getChildren(item);
          _treedata.push(item);
        }
      });
      this.$nextTick(() =&gt; {
        this.initCheckedData();
      });
      return _treedata;
    },
    checkedKeys() {
      let val = this.selectedVal;
      if (!val) {
        return [];
      }
      if (!isArray(val)) {
        val = [val];
      }
      return val;
    }
  },
  watch: {
    isShowSelect(val) {
      //如果是显示 先判断禁用状态
      if (val &amp;&amp; this.$refs.select.$children[0].disabled) {
        this.isShowSelect = false;
      }
      // 隐藏select自带的下拉框
      this.$refs.select.blur();
    },
    checkedKeys(val) {
      if (!val) {
        return;
      }
      if (!isArray(val)) {
        val = [val];
      }
      // this.checkedKeys = val;
      this.initCheckedData();
    },
    nameKey: {
      handler(newName, oldName) {
        this.defaultProps.label = newName;
      },
      immediate: true
    },
    width: {
      handler(newName, oldName) {
        this.$nextTick(() =&gt; {
          this.$refs.tree.$el.style.width =
            this.$refs.select.$el.offsetWidth - 24 + &quot;px&quot;;
        });
      },
      immediate: true
    }
  }
};
&lt;/script&gt;

&lt;style scoped&gt;
.mask {
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  opacity: 0;
  z-index: 11;
}
.common-tree {
  overflow: auto;
}
.tree-select {
  z-index: 111;
}
&lt;/style&gt;
</code></pre>
<p>使用</p>
<pre><code>&lt;el-form-item label=&quot;所在部门&quot; prop=&quot;System_1_170&quot;&gt;
  &lt;select-tree
    :disabled=&quot;treedisabled&quot;
    v-model=&quot;form.System_1_170&quot;
    width=&quot;100%&quot;
    :data=&quot;sys2Data&quot;
    idKey=&quot;System_2_10&quot;
    pIdKey=&quot;System_2_70&quot;
    nameKey=&quot;System_2_30&quot;
  &gt;&lt;/select-tree&gt;
&lt;/el-form-item&gt;
</code></pre>
<h4 id="2dialog">2.dialog</h4>
<p>弹出框也常用，编辑表单啥的，也单出来了，需求是表单外面不想套<el-dialog>还得复制粘贴，还得写参数控制显示关闭，我既然表单是组件了，那我调用方法，调用指定组件给我弹出来就行了，这里没有选择动态子组件或者插槽，因为当时不会</p>
<p>两层，一个是组件，一个是方法</p>
<pre><code>&lt;template&gt;
  &lt;el-dialog
    :title=&quot;title&quot;
    :visible.sync=&quot;dialogVisible&quot;
    :close-on-click-modal=&quot;false&quot;
    :close-on-press-escape=&quot;false&quot;
  &gt;
    &lt;div ref=&quot;component&quot; id=&quot;component&quot;&gt;&lt;/div&gt;
  &lt;/el-dialog&gt;
&lt;/template&gt;

&lt;script&gt;
import Vue from &quot;vue&quot;;
export default {
  data: () =&gt; ({
    component: null,
    title: &quot;编辑&quot;,
    dialogVisible: false,
    options: {},
    instance: {}
  }),
  mounted() {},
  watch: {},
  methods: {
    show(opt) {
      this.dialogVisible = true;
      //把Dialog自己作为参数传过去
      let data = Object.assign(this.instance, opt, { thisDialog: this });
      this.$nextTick(() =&gt; {
        //绘制内部组件
        const component = Vue.extend(this.component);
        this.instance = new component({
          el: &quot;#component&quot;,
          data: { ...data }
        });
      });
      return this;
    },
    close() {
      this.dialogVisible = false;
    }
  }
};
&lt;/script&gt;

&lt;style&gt;&lt;/style&gt;
</code></pre>
<p>外面再包一层</p>
<pre><code>/**
 * @description 打开一个window
 * @param {Object} com 组件
 * @param {String} title 标题
 * @param {Object} data 参数
 */
export const window = function(com, title, data = {}) {
  if (!com) {
    return;
  }
  const component = Vue.extend(windowComponent);
  let option = Object.assign(
    { component: com },
    { title },
    { options: { ...data } }
  );
  // console.log(option);
  let instance = new component({
    el: document.createElement(&quot;div&quot;),
    data: option
  });
  this.$el.appendChild(instance.$el);
  return instance;
};
</code></pre>
<p>使用</p>
<pre><code>    //打开页面
    sys10edit(item) {
      this.getsys10edit().show({ form: { ...item } });
    },
    //打开角色编辑
    getsys10edit() {
      let edit = this.$window(sys10Edit, &quot;编辑角色&quot;);
      edit.$on(&quot;onSys10Save&quot;, (res, data) =&gt; {
        res.MsRbool &amp;&amp; this.getsys10data();
      });
      return edit;
    },
</code></pre>
<pre><code>&lt;script&gt;
import { System10 } from &quot;@/api&quot;;
export default {
  data() {
    return {
      form: {},
      thisDialog: {}
    };
  },
  //外部属性
  props: {},
  //内部方法
  methods: {
    initPage() {},
    //关闭
    close() {
      this.thisDialog.close();
    },
    //保存
    async onSave() {
      let res = await System10.Save(this.form);
      res.strMS &amp;&amp; this.$message(res.strMS);
      this.thisDialog.$emit(&quot;onSys10Save&quot;, res, this.form);
      res.MsRbool &amp;&amp; this.close();
    }
  },
  //组件
  components: {},
  //初始化 异步加async await
  mounted() {
    this.initPage();
  },
  //计算属性
  computed: {},
  //监视
  watch: {}
};
&lt;/script&gt;
</code></pre>

            </div>
            
              <div class="tag-container">
                
                  <a href="https://shixixiyue.gitee.io/tag/TyVX6hfCJ/" class="tag">
                    Vue笔记
                  </a>
                
              </div>
            
            
              <div class="next-post">
                <div class="next">下一篇</div>
                <a href="https://shixixiyue.gitee.io/vue-bi-ji-1/">
                  <h3 class="post-title">
                    Vue笔记（1）
                  </h3>
                </a>
              </div>
            

            

          </div>

        </div>
      </div>
    </div>

    <script src="https://unpkg.com/aos@next/dist/aos.js"></script>
<script type="application/javascript">

AOS.init();

var app = new Vue({
  el: '#app',
  data: {
    menuVisible: false,
  },
})

</script>


  <script src="//jsd.onmicrosoft.cn/gh/highlightjs/cdn-release@11.5.1/build/highlight.min.js"></script>
  <script>
    hljs.initHighlightingOnLoad();
    //hljs.registerLanguage('mermaid', window.hljsDefineMermaid);
  </script>
  <script src="//jsd.onmicrosoft.cn/npm/highlightjs-line-numbers.js@2.8.0/dist/highlightjs-line-numbers.min.js"></script>
  <script>
    hljs.initLineNumbersOnLoad({
      singleLine: true
    });
  </script>
 <script src="https://jsd.onmicrosoft.cn/npm/mermaid/dist/mermaid.min.js"></script>
   <script>
    mermaid.initialize({
      startOnLoad: true,
      securityLevel: 'loose',
    });
  </script>





  </body>
</html>
